/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.commands.connection;

import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.Shape;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.uml2.uml.Element;

import com.cea.papyrus.core.helper.ModelerHelper;
import com.cea.papyrus.core.pseudoelement.EnhancedType;
import com.cea.papyrus.core.pseudoelement.UMLElementFactory;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;



/**
 * A command to reconnect a connection to a different start point or end point.
 * The command can be undone or redone.
 * <p>
 * This command is designed to be used together with a GraphicalNodeEditPolicy.
 * To use this command propertly, following steps are necessary:
 * </p>
 * <ol>
 * <li>Create a subclass of GraphicalNodeEditPolicy.</li>
 * <li>Override the <tt>getReconnectSourceCommand(...)</tt> method.
 * Here you need to obtain the Connection model element from the ReconnectRequest,
 * create a new ConnectionReconnectCommand, set the new connection <i>source</i> by calling
 * the <tt>setNewSource(Shape)</tt> method and return the command instance.
 * <li>Override the <tt>getReconnectTargetCommand(...)</tt> method.</li>
 * Here again you need to obtain the Connection model element from the ReconnectRequest,
 * create a new ConnectionReconnectCommand, set the new connection <i>target</i> by calling
 * the <tt>setNewTarget(Shape)</tt> method and return the command instance.</li>
 * </ol>
 * 
 * @see org.eclipse.gef.examples.shapes.parts.ShapeEditPart#createEditPolicies() for an
 * example of the above procedure.
 * @see org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy
 * @see #setNewSource(Shape)
 * @see #setNewTarget(Shape)
 * @author Elias Volanakis
 */
public class ConnectionReconnectCommand extends ConnectionReconnectCommandBase {
	
	/**
	 * 
	 */
	protected Class type=null;
	
	/**
	 * 
	 */
	protected Element umlLink=null;
	
	/**
	 * Instantiate a command that can reconnect a GraphEdge instance to a different source
	 * or target endpoint.
	 * 
	 * @param newEndpoint 
	 * @param end 
	 * @param conn the connection instance to reconnect (non-null)
	 * @param newLocation 
	 * 
	 * @throws IllegalArgumentException if conn is null
	 */
	public ConnectionReconnectCommand(GraphEdge conn, GraphElement newEndpoint, Point newLocation, int end) {
		super(conn, newEndpoint, newLocation, end);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#canExecute()
	 */
	/**
	 * 
	 * 
	 * @return 
	 */
	@Override
	public boolean canExecute() {

		if ((getNewSource() == null) && (getNewTarget() == null)){
			// we can : it is only a anchor move !!!!
			return true;
		}

		if((Uml1SemanticModelBridge) getEdge().getSemanticModel() == null) {
			// this  is a CommentLink or a ConstraintLink =>
			
			// Source moved or target moved
			if ((getNewSource() != null) || (getNewTarget() != null)) {
				if(getNewSource().equals(getOldSource())&&getNewTarget().equals(getOldTarget())){
				/*if ((getNewSourceElement() instanceof Comment)
						|| (getNewSourceElement() instanceof Constraint)
						|| (getNewTargetElement() instanceof Comment)
						|| (getNewTargetElement() instanceof Constraint)
						|| (getNewSourceElement() instanceof TimeObservation)
						|| (getNewTargetElement() instanceof TimeObservation)
						|| (getNewSourceElement() instanceof DurationObservation)
						|| (getNewTargetElement() instanceof DurationObservation)) {*/

					return true;
				}
			}		
			return false;
		}
		else {
			if( getEdge().getSemanticModel()!=null){
				umlLink=getEdgeElement();
				type = ((Uml1SemanticModelBridge) getEdge().getSemanticModel()).getElement().getClass().getInterfaces()[0];
			}
		}

		List<EnhancedType> matchingTypeDescriptions = EnhancedType.getExistingObjectDescriptions(getEdge());

		boolean noRestriction = true;
		Iterator<EnhancedType> matchTypeIt = matchingTypeDescriptions.iterator();
		while(matchTypeIt.hasNext()) {
			EnhancedType currentType = matchTypeIt.next();
			if (currentType instanceof UMLElementFactory) {
				UMLElementFactory umlEType = (UMLElementFactory) currentType;
				noRestriction = umlEType.canExecute(getNewSource(), getNewTarget());

				// On first error : abort and return null
				if (!noRestriction) {
					return false;
				}
			}
		}
		Element sourceElt= getNewSourceElement();

		//the case where we display properties of stereotype
		if( getNewTarget().getSemanticModel()==null && getNewTarget().getSemanticParent().equals(getNewSource())){
			return true;
		}


		return ModelerHelper.getFactory().canConnect(type, getNewSourceElement(), getNewTargetElement());
	}




	/**
	 * Reconnect the connection to getNewSource() (if setNewSource(...) was invoked before)
	 * or getNewTarget() (if setNewTarget(...) was invoked before).
	 */
	@Override
	public void executeOnBussiness() {
		if(getNewTarget().getSemanticModel()!=null ||getNewSource().getSemanticModel()!=null ){
			ModelerHelper.getFactory().deconnect(type, getOldSourceElement(), getOldTargetElement(),umlLink);
			ModelerHelper.getFactory().connect(type, getNewSourceElement(), getNewTargetElement(), umlLink);
		}
	}



	/**
	 * Reconnect the connection to its original source and target endpoints.
	 */
	@Override
	public void undoOnBussiness() {
		if(getNewTarget().getSemanticModel()!=null ||getNewSource().getSemanticModel()!=null ){
			ModelerHelper.getFactory().deconnect(type, getNewSourceElement(), getNewTargetElement(),umlLink);
			ModelerHelper.getFactory().connect(type, getOldSourceElement(), getOldTargetElement(), umlLink);
		}
	}
}
